Shader Reloader Macro topic

ShaderReloaderMacro

ShaderReloaderMacro is a builtin asset macro that compiles Flutter GPU shader bundles. It watches a directory of shader sources and, whenever a file changes (add, modify or remove), locates every *.shaderbundle.json manifest in that directory and re-runs impellerc to produce a compiled .shaderbundle.

The compiled bundles are written into the configured asset macro output directory (for example build/shaderbundles), so a running Flutter app can pick up rebuilt shaders on hot reload without any manual build step — pair it with the onMacroFilesGenerated stream to react instantly.

The macro is pure Dart (dart:io + process spawn) and has no Flutter dependency.

Features

  • Automatic Recompilation: Re-runs impellerc whenever any watched shader file changes
  • Multi-Manifest Support: Compiles every *.shaderbundle.json manifest in the watched directory (including nested subdirectories)
  • Cross-Platform Compiler Discovery: Locates impellerc for macOS (arm64/x64), Linux and Windows automatically
  • Engine Shader Library: Passes the engine's shader_lib directory via --include, so shaders can use framework includes like <flutter_gpu/framebuffer.frag.h>
  • Live Push Events: The generated bundle paths are pushed to connected apps through onMacroFilesGenerated

Setup

Register the macro in your project's macro entrypoint:

await runMacro(
  macros: {
    'ShaderReloaderMacro': ShaderReloaderMacro.initialize,
  },
  assetMacros: {
    'shaders': [
      AssetMacroInfo(
        macroName: 'ShaderReloaderMacro',
        // The server matches single-level extensions, so `.json`
        // also covers `.shaderbundle.json` manifests.
        extension: '.vert,.frag,.json',
        output: 'build/shaderbundles',
        config: const ShaderReloaderConfig().toJson(),
      ),
    ],
  },
);

Configuration Parameters

macroName

  • Type: String
  • Required: Yes
  • Description: The name of the macro to use. Must be 'ShaderReloaderMacro'.

extension

  • Type: String
  • Required: Yes
  • Description: File extension filter for the watched shader sources. Use something like '.vert,.frag,.json' to watch shader sources plus manifests.

output

  • Type: String
  • Required: Yes
  • Description: Directory where compiled .shaderbundle files are written (e.g. 'build/shaderbundles').

config

  • Type: Map<String, dynamic>?
  • Required: false
  • Description: Configuration object for the ShaderReloaderMacro. Must be converted to JSON using .toJson().

ShaderReloaderConfig Options

final config = const ShaderReloaderConfig(
  manifestExtension: '.shaderbundle.json',
  compilerPath: '/path/to/impellerc',
  flutterRoot: '/path/to/flutter',
);

manifestExtension

  • Type: String?
  • Default: '.shaderbundle.json'
  • Description: Suffix that identifies shader bundle manifests within the watched directory.

compilerPath

  • Type: String?
  • Default: null
  • Description: Explicit path to the impellerc executable. When set, it takes precedence over all automatic discovery strategies.

flutterRoot

  • Type: String?
  • Default: null
  • Description: Explicit path to the Flutter SDK root used to locate impellerc and the engine shader_lib includes. Takes precedence over FLUTTER_ROOT and ephemeral configs.

Manifest Format

A manifest declares which shader sources belong to a bundle. Each key becomes the shader name, with its type (vertex or fragment) and source file relative to the manifest:

{
  "CubeVertex": {
    "type": "vertex",
    "file": "cube.vert"
  },
  "CubeFragment": {
    "type": "fragment",
    "file": "cube.frag"
  }
}

The compiled output keeps the manifest's base name: flutter_example.shaderbundle.json produces flutter_example.shaderbundle inside the configured output directory.

How it finds impellerc

  1. ShaderReloaderConfig.compilerPath explicit override
  2. ShaderReloaderConfig.flutterRoot explicit override
  3. FLUTTER_ROOT environment variable
  4. FLUTTER_ROOT= inside the project's platform-specific ephemeral config generated by the flutter tool (Flutter-Generated.xcconfig on macOS/iOS, generated_config.cmake on Linux/Windows)

For each candidate root it probes the engine artifacts directory bin/cache/artifacts/engine/<target>/impellerc, where <target> matches the host machine (darwin-arm64/darwin-x64 on macOS, linux-x64 on Linux, windows-x64 on Windows).

Hot Reloading In A Running App

Listen to the generated files stream after connecting and reinitialize your shader library:

onMacroFilesGenerated.listen((files) {
  final bytes = File(bundlePath).readAsBytesSync();
  library.reinitializeFromBytes(ByteData.sublistView(bytes));
});

Saving any watched shader source now recompiles the bundle and recolors/re-renders the running app without a rebuild.


Next: Embed Macro